// Copyright (c) MatrixFramework. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Matrix.ServiceCommunication.Services;

namespace Matrix.ServiceCommunication.Discovery.LoadBalancing;

/// <summary>
/// 最少连接数负载均衡器
/// </summary>
public class LeastConnectionsLoadBalancer : ILoadBalancer
{
    private readonly ConcurrentDictionary<string, int> _connections = new();

    public string StrategyName => "LeastConnections";

    public ServiceInstance Select(IList<ServiceInstance> instances)
    {
        if (instances == null || !instances.Any())
        {
            throw new ArgumentException("实例列表不能为空", nameof(instances));
        }

        var healthyInstances = instances.Where(i => i.Healthy && i.Enabled).ToList();
        if (!healthyInstances.Any())
        {
            throw new InvalidOperationException("没有可用的健康实例");
        }

        // 选择连接数最少的实例
        return healthyInstances
            .OrderBy(instance => _connections.GetOrAdd(instance.ServiceId, _ => 0))
            .ThenBy(instance => instance.Weight) // 连接数相同时按权重排序
            .First();
    }

    /// <summary>
    /// 增加实例连接数
    /// </summary>
    /// <param name="serviceId">服务实例ID</param>
    public void IncrementConnections(string serviceId)
    {
        if (string.IsNullOrEmpty(serviceId)) return;

        _connections.AddOrUpdate(serviceId, 1, (key, value) => value + 1);
    }

    /// <summary>
    /// 减少实例连接数
    /// </summary>
    /// <param name="serviceId">服务实例ID</param>
    public void DecrementConnections(string serviceId)
    {
        if (string.IsNullOrEmpty(serviceId)) return;

        _connections.AddOrUpdate(serviceId, 0, (key, value) => Math.Max(0, value - 1));
    }

    /// <summary>
    /// 重置实例连接数
    /// </summary>
    /// <param name="serviceId">服务实例ID</param>
    public void ResetConnections(string serviceId)
    {
        if (string.IsNullOrEmpty(serviceId)) return;

        _connections.AddOrUpdate(serviceId, 0, (_, _) => 0);
    }
}